'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 } //--> Inside AutoCAD 14 -- Ch 24 -- Introduction to AutoLISP Pprogramming


Inside AutoCAD 14

Previous chapterNext chapterContents


- 24 -

Introduction to AutoLISP Pprogramming


by Michael Todd Peterson

One of AutoCAD's most powerful features is its programming interface. With support for AutoLISP, ADS, ARX, and script programming, you can customize AutoCAD to just about any degree you want.

This chapter focuses on the basics of AutoLISP; its intent is to introduce you to AutoLISP's capabilities. The chapter covers the following topics:

Introducing AutoLISP

AutoLISP is an interpreted language, not a compiled language. An interpreted language is one in which an interpreter converts the source code, or program, into machine language at run time. In AutoCAD's case, the interpreter is built in. A compiled language is one in which a compiler converts the source code, or program, into machine language before you can use the program. An example of a compiled language is C++, which Release 14 uses in its ARX 2.0 programming interface. Another example is C, which AutoCAD uses in the older ADS programming interface, and which it continues to make available in Release 14.

Because AutoLISP is an interpreted language, you can use any text editor you want for creating the AutoLISP routine. Then, to test the AutoLISP routine, you can load it into AutoCAD and run it. AutoLISP routines can be used to automate any of a wide variety of mundane repetitive tasks or they can be used to add functionality to AutoCAD. For example, you may find yourself repeating the same commands over and over again in the same order in your daily work. You may find it more advan-tageous to spend some time and write an AutoLISP routine that either automates the task or creates a new method of handling the task.

The following is a sample of a simple AutoLISP routine:

(defun C:ZI ()
       (command "zoom" ".5x")  ;begin zoom command and its scale
(princ)
)

At first, this might look confusing, but it is rather simple. All AutoLISP routines use the same basic features. The preceding routine consists of three parts: a function, a statement or expression, and a command.

A function is a command or series of commands that AutoCAD executes in a particular order. Each function must have a name, so that other functions can call it using that name. The preceding example is one function. A function is defined by the defun statement, followed by the function name. In this case, if C: is used before the function name, then that is the command you can use inside of AutoCAD to run this AutoLISP routine. So, after this AutoLISP routine is loaded, you can run it by typing in ZI.

Statements and expressions are a part of a larger function. An expression executes one single command or operation. Expressions can do anything from getting a point from the mouse to calculating the sine of an angle. In the preceding example, (command "zoom" ".5x") is the expression. In this example, command function is called. This function executes standard AutoCAD commands. This function executes the Zoom command followed by .5x.

A command is a call to another command or function that is either predefined in AutoLISP or predefined in your program. In this case, the (princ) statement is a command calling a predefined AutoLISP command. The princ statement is necessary to have the AutoLISP routine exit correctly without any error messages.

By combining one or more functions, you can create simple to complex AutoLISP routines that can automate many different functions inside AutoCAD.

Formatting an AutoLISP Routine

When you begin to write an AutoLISP routine, the format you write in is rather important. In the preceding example, you will notice that each part of the AutoLISP routine is divided by parentheses. The location of parentheses enables you to define which parts of the program are working at what times. The following is an example of a simple AutoLISP routine showing you how parentheses are being used:

(defun C:ZI ()
       (command "zoom" ".5x")
(princ)
)

For example, notice that the complete AutoLISP routine is contained within a set of parentheses, as highlighted in this example. A function is contained completely within parentheses as well.

Each function has the option of taking arguments. Arguments are variables that are passed to the function for use at run time. For example, if you have an AutoLISP routine that draws a ten foot long wall, the number 10 would be an argument to the function that generates the wall. Arguments are placed in the bold parentheses in the following example. An argument is a value that the calling function passes to the function. For the purposes of this chapter, you do not use arguments.

(defun C:ZI ()
       (command "zoom" ".5x")
(princ)
)

Then, each statement in the AutoLISP routine is separated completely with parentheses as shown in bold in the following example.

(defun C:ZI ()
       (command "zoom" ".5x")
(princ)
)


NOTE: The AutoLISP routine is organized with indentations to help clarify visually which statements are part of what commands. You could also write the preceding AutoLISP routine like this:

(defun C:ZI ()(command "zoom" ".5x")(princ))
As you can see, the first example is easier to read and keep track of.


The last thing that also helps you a great deal as a novice programmer is the addition of comments. If you start a line with a semicolon (;), the AutoLISP interpreter ignores that line. The following example has a comment in it:

(defun C:ZI ()
; Call the command function to execute AutoCAD commands
       (command "zoom" ".5x") ;begin zoom command and its scale
(princ)
)

Using comments in your programs is highly encouraged. The reason is simple. Say, for example, you write a few AutoLISP routines over the next few weeks. Then, three years from now, you decide to update and increase the functionality of those routines. Those comments in the AutoLISP routines make it much easier to remember what in the world you were doing in this or that routine. Otherwise, you probably would be better off starting from scratch.


NOTE: This chapter only scratches the surface of AutoLISP functionality. For more information on formatting and usage of AutoLISP, see the AutoCAD customization manual shipped with AutoCAD.

What's New in AutoLISP in Release 14?

Like many other features of AutoCAD, Autodesk basically has left the AutoLISP interface unchanged. The only improvement to AutoLISP comes on the user side, not the programming side. In previous versions of AutoCAD, if you loaded or started a new drawing, AutoCAD would process the ACAD.LSP file and reload all the necessary AutoLISP routines. That's not how it works in Release 14. All the AutoLISP routines remain loaded in memory and become immediately available when the user loads or creates the drawing, resulting in much faster load times and greater productivity for users.

Now that you have an idea of AutoLISP's structure and what's new in AutoLISP in AutoCAD Release 14, you are ready for an introduction to the exact use of AutoLISP through example.

Using AutoLISP for Keyboard Macros

As you saw in Chapter 22, "Customizing without Programming," you could quickly and easily create simple keyboard macros by modifying the ACAD.PGP file. But these keyboard macros had their limitations. For example, you could execute only one command per keyboard shortcut.

In AutoCAD, many commands are combinations of smaller commands. Take, for example, the Zoom command. Before you can use the Zoom command, you must type zoom, and then type in an option for the command--only then will it work. In the ACAD.PGP file, you could simplify access to the Zoom command, but not the options. Here comes AutoLISP to the rescue!

You can use the AutoLISP programming interface to develop other keyboard shortcuts quickly and easily, as in the following example:

(defun C:ZO ()
        (command "zoom" "2x")
(princ)
)

In this example, you define a keyboard macro that executes with the keyboard shortcut ZO. In AutoLISP, this keyboard shortcut is defined as a function. The C:ZO indicates that you can use the ZO shortcut from inside AutoCAD to run the routine.

The (command "zoom" "2x") simply executes the commands in the order you would have to at the Command: prompt to execute the same commands. In this case, the routine enters the Zoom command, followed by the 2x option for the Zoom command.

To see how this works, the following exercise shows you how to load the AutoLISP routine and run it. The AutoLISP routine is provided on the accompanying CD-ROM as the file ZO.LSP. Alternatively, you can type this routine into your favorite text editor and save it as ZO.LSP.


NOTE: All AutoLISP routines must have an .LSP extension.

LOADING AND RUNNING A SAMPLE AUTOLISP ROUTINE: ZOOM

1. Load AutoCAD and any sample drawing.

2. Choose Tools, Applications as shown in figure 24.1 to load the AutoLISP dialog box.

Figure 24.1 The Load AutoLISP, ADS, and ARX Files dialog box.

3. Click on the File button, which displays a standard Windows file selection dialog box, as shown in figure 24.2.

Figure 24.2 The Select AutoLISP, ADS, or ARX File dialog box.

4. Select the ZO.LSP routine from the CD.

5. Click on OK to add the routine to the list.

6. Click on the Load button to load the AutoLISP routine.

7. Type ZO at the Command: prompt to execute the AutoLISP routine.

After you load the routine, it stays available (only if persistent AutoLISP is enabled) until you exit AutoCAD or load another routine that uses the same keyboard shortcut.

Creating a Single AutoLISP Routine for Multiple Shortcuts

As you can see, creating keyboard macros is rather easy using AutoLISP. You can generate a single AutoLISP routine to contain all your keyboard macros. Suppose, for example, you want to create a set of macros for the Zoom command and another one that sets your fillet radius to 0. As you know, if you set a radius during the Fillet command, it dumps you out, forcing you to reload it. The following example shows you an AutoLISP routine that has three keyboard shortcuts in it: two Zoom shortcuts and one Fillet shortcut. The fillet shortcut creates a fillet with a radius of 0.

(defun C:ZW ()
       (command "zoom" "W")
       (princ)
)
(defun C:ZE ()
       (command "zoom" "e")
       (princ)
)
(defun C:F0 ()
       (command "fillet" "r" "0" "fillet")
       (princ)
)

If you load this program into AutoCAD, all three keyboard macros are available to you. This AutoLISP routine is saved on the CD as IACAD142.LSP. To create other keyboard macros, simply use IACAD142.LSP as a template. All you need to know is the exact order in which you must type the commands at the Command: prompt.

Loading an AutoLISP Routine with AutoCAD

Because you have now seen several keyboard macros using AutoLISP, you probably would like to have them available every time you load AutoCAD instead of having to load them with the APPLOAD command. Well, AutoCAD provides just such an interface: the ACAD.LSP routine.

If you do not have an ACAD.LSP file on your system, you must create it. You must place this file in one of your AutoCAD library paths. Usually, the SUPPORT directory works fine.


TIP: You may want to search your hard drive to make sure that another ACAD.LSP file is not hiding somewhere. If another ACAD.LSP file is on the system, it may get loaded instead of yours. If you find another one, you can simply add these statements to the end of that file. An easy way to find any available ACAD.LSP file is to use the following short command sequence.
At the Command: prompt type:

(findfile "acad.lsp")

This will return the name and location of the first available ACAD.LSP or nil if none are in the AutoCAD search path. It probably should be the one for you to edit.


To load an AutoLISP routine, simply add the following statement to the ACAD.LSP file:

(load "X:/path/LISP")

For the preceding statement, X: is the drive where the AutoLISP routine is located, path is the location of the file on the hard drive, and LISP is the name of the AutoLISP routine without its extension. The path and drive letters are necessary if you save your AutoLISP routine in a directory other than one in the AutoCAD search path. If you do specify a path, make sure you use a forward slash instead of a backslash for specifying your directories. The file will now load automatically every time AutoCAD is loaded.

Creating a Simple AutoLISP Routine

Now that you have seen how to use AutoLISP in a simple context, it is time to look at a more complex example. The following example shows you how you can use an AutoLISP routine to generate a single wall with BATT insulation. After the code is provided, this section walks you through the entire routine, explaining each section in detail.

The example is saved in completed form on the CD as IACAD14.LSP.

The following is the code for this example:

(defun dtr (a)
       (* pi (/ a 180.0))
)
(defun info ()
(setq start (getpoint "\nChoose the start point of the wall: "))
       (setq end (getpoint "\nChoose the end point of the wall: "))
       (setq width (getdist "\nEnter the width: " start))
       (setq halfwidth (/ width 2))
       (setq wallangle (angle start end))
       (setq walllength (distance start end))
       (setq arcsize (/ width 4))
       (setq perpath (+ wallangle (dtr 90)))
       (setq perpath2 (- wallangle (dtr 90)))
       (setq total 1)
       (setq arcnumber 1)
)
(defun drawwall ()
       (command "pline"
               (setq corner (polar start perpath halfwidth))
               (setq corner (polar corner wallangle walllength))
               (setq corner (polar corner perpath2 width))
               (polar corner (+ wallangle (dtr 180)) walllength)
               "close"
               )
)
(defun insulate ()
       (setq arcstart1 (polar start perpath2 (/ halfwidth 2)))
       (while (< total walllength)
               (setq arcenter1 (polar arcstart1 wallangle arcsize))
               (setq arcend1 (polar arcenter1 wallangle arcsize))
               (setq arcenter2 (polar arcend1 perpath (* 2                 ¬arcsize)))
               (setq arcstart2 (polar arcenter2 wallangle arcsize))
               (setq arcend2 (polar arcenter2 (+ wallangle (dtr                 ¬180)) arcsize))
               (command "arc" arcstart1 "c" arcenter1 arcend1)
               (command "arc" "c" arcenter2 arcstart2 arcend2)
               (command "line" arcstart2 arcend1 "")
               (command "line" arcend2 arcend1 "")
               (setq arcnumber (1+ arcnumber))
               (setq arclength (distance arcstart1 arcend1))
               (setq arcstart1 (polar arcstart1 wallangle (* 2                 ¬arcsize)))
               (setq total (* arcnumber  arclength))
       )
)
(defun C:IACAD14()
       (info)
       (drawwall)
       (insulate)
       (princ)
) 

This AutoLISP routine is broken down into the following five functions:

Figure 24.3 shows you the result of running the AutoLISP routine.

Figure 24.3 A wall created by executing IACAD14.LSP.

Converting Degrees to Radians with the DTR Function

The first function in the AutoLISP routine is the DTR function. Its source code follows:

(defun dtr (a)
       (* pi (/ a 180.0))
)

As you can see, it is a fairly simple little function. It is the only function that takes an argument. In this case, the argument is a. This function takes a number that is in degrees and converts it to radians. AutoLISP functions only use radians, so you must make this computation. Look at the second line of the program, which is the one that performs the calculation.

(* pi (/ a 180.0))

In this equation, pi is multiplied by a/180. In other words, when a degree measure is passed to this function, it is divided by 180 and then multiplied by pi. AutoLISP requires that you place the operator first in the equation, and then follow it with the first and second arguments.

So, the first operator is an asterisk (*), which is multiplication. The first argument is pi, which is 3.14159. Pi is a predefined value in AutoLISP because it gets used so often. Then, in the parentheses, you have the second argument, which is another equation. In this case, the operator is a slash (/), which is division. The first argument is a, the passed variable, and the second argument is 180.0. (180.0 includes the decimal point to ensure accurate floating-point calculations.)

This function is used throughout the AutoLISP routine to calculate the radian equivalent of degrees. As it happens, this is necessary to calculate the angle at which your wall is running.

Gathering Information with the Info Function

The second function in the AutoLISP routine is the information gathering function. In this function, all the necessary information for the routine is gathered or calculated. The following is the info function:

(defun info ()
    (setq start (getpoint "\nChoose the start point of the wall: "))
       (setq end (getpoint "\nChoose the end point of the wall: "))
       (setq width (getdist "\nEnter the width: " start))
       (setq halfwidth (/ width 2))
       (setq wallangle (angle start end))
       (setq walllength (distance start end))
       (setq arcsize (/ width 4))
       (setq perpath (+ wallangle (dtr 90)))
       (setq perpath2 (- wallangle (dtr 90)))
       (setq total 1)
       (setq arcnumber 1)
)

In this function, you set 11 different statements, or expressions. In AutoLISP, you can have something called a variable. A variable has a value assigned to it. Each of the 11 statements assigns a value to a different variable for use later in the program. Some statements assign a simple numeric value, whereas others assign the result of a mathematical equation to the variable. Take the first expression for example:

(setq start (getpoint "\nChoose the start point of the wall: "))

In this statement, you have an AutoLISP command called setq, which is the command to set a variable to a specific value. In this case, the variable is called start. In the parentheses, you have another AutoLISP command called getpoint. The getpoint command returns the x, y, and z coordinates of a mouse click.

The \n is a control code character. Any expression that uses a slash (\) has a control code character after it. In this case, the n represents the newline character. The information following the \n simply prints to the AutoCAD Command: prompt area. Figure 24.4 shows you the Command: prompt area when you run the IACAD14.LSP file and reach this point.

Figure 24.4 The Command: prompt at the start of the AutoLISP routine.

This statement asks the user to choose the start point of the wall with the mouse or by typing in the coordinates. The x, y, and z coordinates of the start point of the wall are assigned to the variable start.


TIP: When you have run the wall routine inside AutoCAD, you can check the value of the variable start. At the Command: prompt, simply type !start. This applies to all variables created in this AutoLISP routine. Simply type in an exclamation point followed by the variable name, and AutoCAD displays the value of that variable. A nil value means nothing has been assigned to the variable. Remember this technique--it's invaluable in debugging the program.

The next line of the AutoLISP routine gets the endpoint of the wall in the same manner and assigns it to the variable end. But, the third line does something a little different. The following is the third expression in the function:

(setq width (getdist "\nEnter the width: " start))

Here, the routine prompts the user for the width of the wall and assigns it to the variable width. What is different here is the getdist command. The getdist command calculates the distance between a chosen point and a stored point. In this case, the width of the wall is defined by the user choosing a point on the screen with the mouse. AutoCAD calculates the distance between that point and the start point of the wall and stores it in the width variable. That is why the start variable appears at the end of this statement.

The fourth statement also does something a little different. The fourth statement is as follows:

(setq halfwidth (/ width 2))

In this statement, a variable called halfwidth is assigned to the result of an equation. Here, the variable width is divided by 2 and assigned to halfwidth. Later in the AutoLISP routine, it will be necessary to know the halfwidth of the wall.

The fifth statement uses a slight variation on the fourth statement to assign a value to another variable. The fifth statement is as follows:

(setq wallangle (angle start end))

This line defines the wallangle variable. The angle command calculates the angle between two points, in this case, the start point and the end point of the wall. This value is then assigned to the wallangle variable.

The next two statements in the AutoLISP routine are simple variations. But, look at the eighth statement. Here, the variable perpath is assigned a value. The statement is as follows:

(setq perpath (+ wallangle (dtr 90)))

Here, you are again assigning the result of an equation to a variable. In this case, you are calling another function. Notice the dtr 90 statement in parentheses. This function calculates the angle that is perpendicular to the angle of the wall. In this case, 90 degrees is sent to the dtr function and converted to radians. In the dtr equation, the 90 degrees takes the place of the a variable, which is then added to the wallangle value and assigned to the perpath variable.

The ninth statement performs a function similar to that of the eighth statement, except that it calculates the perpendicular angle in the opposite direction. So, a - operator is used instead of a +.

The tenth and eleventh statements are sometimes called initialization statements. All they do is initialize a variable to a value, so that it is not nil. In this case, the total and arcnumber variables are initialized to 1. Why this is done becomes evident later in the routine. Each statement appears as follows:

(setq total 1)
(setq arcnumber 1)

That completes the info routine. When the AutoLISP routine runs the info routine, either the user inputs or AutoCAD calculates all necessary information. Now, it is time to actually begin drawing the wall. There are two parts to the wall drawing routine. The first part draws the outline of the wall. The second part draws the insulation inside the wall. By far, drawing the insulation is the hardest part of the routine.

Drawing the Wall Outline with the Drawwall Function

The wall outline is handled by the drawwall function. The listing for the drawwall function is as follows:

(defun drawwall ()
        (command "pline"
               (setq corner (polar start perpath halfwidth))
               (setq corner (polar corner wallangle walllength))
               (setq corner (polar corner perpath2 width))
               (polar corner (+ wallangle (dtr 180)) walllength)
               "close"
               )
)

In this function, you simply execute a drawing command and calculate all the necessary points. Notice the first statement of function. It is as follows:

(command "pline"

This statement simply runs the PLINE command. So, the outline of the wall is generated as a polyline. The second statement draws the first point of the polyline. The second statement also assigns that point to a variable called corner. The statement is as follows:

(setq corner (polar start perpath halfwidth))

In this statement, the variable corner is set to a calculated value. In the parentheses, a polar coordinate is set up. The command polar means you will be using polar coordinate entry methods. So, you have to provide a starting point, an angle, and a distance. In this case, the starting point is the start point of the line, or the start variable. The angle is perpendicular to the wall angle. The distance is half the width of the wall, or the halfwidth variable.

The next statement draws the second part of the line. The statement is as follows:

(setq corner (polar corner wallangle walllength))

Here, the corner variable is redefined. But, before it is redefined, it is used to calculate the new variable. Again, you are drawing another polyline using a polar coordinate method of entering points. Here, the start point is the corner variable from the previous equation. The angle is now the angle of the wall, or the wallangle variable. This line is drawn to the exact length of the wall, as defined in the walllength variable.

The routine continues over the next two lines to draw the rest of the outline of the wall. The last statement of the routine is simply the close statement to create a closed polyline.

After you draw the outline of the wall, you can begin to draw the insulation inside. The AutoLISP routine contains a function called insulate to draw the insulation. But, before learning about that routine, you should be aware of how the insulation is going to be drawn.

Insulating the Wall

The insulation in the wall is drawn from two different drawing elements: an arc and a line. By drawing arcs up and down both sides and connecting the endpoints, you can create the insulation. The only question is the order in which they are drawn.

Take, for example, the wall shown in figure 24.5. The insulation routine begins by drawing an arc at the lower-right corner of the wall.

Figure 24.5 In this example wall, the insulation routine will start at the lower-right corner of the wall.

To draw an arc, you must calculate three points. These points will be defined in the routine as arcstart1, arcenter1, and arcend1. Figure 24.6 shows you a blowup of the lower-right corner, with the arc already drawn using the start point of the wall, arcstart1, arcenter1, and arcend1.

Figure 24.6 The example wall with the first arc.

First, the routine must calculate the location of the arcstart1 point. Then, you can simply draw the arc. But, how large an arc should you draw?

When this routine draws insulation, it is dynamic because it will adjust the size of the insulation to match the width of the wall. This is handled by setting the arcsize to be 1/4 the width of the wall. If you look back to the info routine, you will see an arcsize variable that calculates this information.

Because an arc is nothing more than a segment of a circle, you can easily calculate the arcenter1 and arcend1 points. You just calculate a point that is one arcsize unit away from the arcstart1 point in the same direction of the wall. You calculate the arcend1 point by using twice the arcsize value. Hence, the arc is drawn.

The next item to draw in the insulation is the arc on the other side of the wall. To make this arc, you first need to find its start point. To make the insulation look good, you can place the start point of the second arc perpendicular to the center point of the first arc, as shown in figure 24.7.

Figure 24.7 The example wall with two arcs.

After you calculate the position of the start point of the second arc, you can use a similar routine to calculate the center and end points of the second arc. You can save each of these points to variables named arcstart2, arcenter2, and arcend2.

After you have the second arc, all you have to do is connect the start and end points of the second arc to the end point of the first arc, as shown in figure 24.8.

Figure 24.8 The example wall with two arcs and two lines.

Now, to make that routine repeat, start the first arc next to the previous first arc. Figure 24.9 shows you the wall after the second run of the routine.

Figure 24.9 The example wall after two runs.

Now that you are repeating the same commands, you must figure out when to stop; otherwise, you will be creating insulation forever.

An easy way to calculate when to stop drawing insulation is to keep track of the number of first arcs that you create. Then, you can calculate the distance from the start point to the end point of the first arc. If you multiply those numbers together, you will get the overall length of the insulation. If you compare the length of the insulation to the length of the wall, you can calculate when to stop the insulation routine.


NOTE: This insulation routine represents one of many different ways you could go about setting up this routine. When you finish this chapter, you may try different methods of creating the insulation to get more practice with AutoLISP programming.

Creating Code for the Insulation Routine

Now that you have an idea of how this insulation routine is going to work, you are ready to look at the code. The code for the insulation routine is as follows:

(defun insulate ()
       (setq arcstart1 (polar start perpath2 (/ halfwidth 2)))
       (while (< total walllength)
               (setq arcenter1 (polar arcstart1 wallangle arcsize))
               (setq arcend1 (polar arcenter1 wallangle arcsize))
               (setq arcenter2 (polar arcend1 perpath (* 2                 ¬arcsize)))
               (setq arcstart2 (polar arcenter2 wallangle arcsize))
               (setq arcend2 (polar arcenter2 (+ wallangle (dtr                 ¬180)) arcsize))
               (command "arc" arcstart1 "c" arcenter1 arcend1)
               (command "arc" "c" arcenter2 arcstart2 arcend2)
               (command "line" arcstart2 arcend1 "")
               (command "line" arcend2 arcend1 "")
               (setq arcnumber (1+ arcnumber))
               (setq arclength (distance arcstart1 arcend1))
               (setq arcstart1 (polar arcstart1 wallangle (* 2                 ¬arcsize)))
               (setq total (* arcnumber  arclength))
       )
)

The first statement in the insulate routine calculates the start point of the first arc. This statement should look familiar to you by now. The only thing to notice is the distance in the polar command. It is calculated at half of the halfwidth. So, the first start point is halfway between the start point of the wall and the corner, as shown in figure 24.10.

Figure 24.10 The start point of the first arc.

Now that you have the starting point, you are ready to set up the repeating loop. In this case, a while statement will be used. A while statement works by evaluating an equation. As long as the equation is true, the statements inside the while loop will repeat. When the statement becomes false, the loop is broken and the program moves on. The following statements are inside the while loop:

       (while (< total walllength)
               (setq arcenter1 (polar arcstart1 wallangle arcsize))
               (setq arcend1 (polar arcenter1 wallangle arcsize))
               (setq arcenter2 (polar arcend1 perpath (* 2                 ¬arcsize)))
               (setq arcstart2 (polar arcenter2 wallangle arcsize))
               (setq arcend2 (polar arcenter2 (+ wallangle (dtr 
               ¬180)) arcsize))
               (command "arc" arcstart1 "c" arcenter1 arcend1)
               (command "arc" "c" arcenter2 arcstart2 arcend2)
               (command "line" arcstart2 arcend1 "")
               (command "line" arcend2 arcend1 "")
               (setq arcnumber (1+ arcnumber))
               (setq arclength (distance arcstart1 arcend1))
               (setq arcstart1 (polar arcstart1 wallangle (* 2                 ¬arcsize)))
               (setq total (* arcnumber arclength))

The first five statements of the while loop calculate the other points necessary to draw the first two arcs. The next four statements draw two arcs, followed by two lines.

Notice in the drawing routines, especially the arc routines, that familiarity with how the arc routine works is important. In the first arc statement, the command arc is executed and the first point is entered. The center option is chosen before the second point is entered.

At this point, you should be able to decipher what is happening in the first nine statements of this function. Next, the arcnumber value is initialized and set. This value counts how many times you run through this routine. Each time you run through the routine, you create one first arc.

The next statement calculates the arclength by calculating the distance between the start and end points of the first arc. Now that you have the arcnumber and the length of the arcs, you can calculate the overall length of the insulation. Notice the last statement of the routine. That is precisely what happens. In the while loop, the variable total is compared to the walllength. If the total variable is less than the walllength variable, the routine will be run again. Otherwise, the routine is ended.

Now, one statement has been skipped. This statement is as follows:

(setq arcstart1 arcend1)

This statement calculates the position of the start point of the first arc for the next run through of the insulation routine. In this case, the value is set to the end point of the first arc. So, the next time the routine starts, it will begin drawing at the point shown in figure 24.11.

Figure 24.11 The example wall showing the second loop start point.

Running the Functions with the IACAD14 Command

All but one of the necessary functions in the AutoLISP routine has been completed. The last function is the easiest; it is the function that runs all the other functions. The code listing is as follows:

(defun C:IACAD14()
        (info)
        (drawwall)
        (insulate)
        (princ)
)

As you can see, with the use of the C:, this routine is started by the IACAD14 command in AutoCAD, after the routine is loaded. Then, each function is called in the appropriate order. First, you collect the information, so the info routine is called. Second, you draw the outline of the wall, so the drawwall function is called. Third, you call the insulate routine to draw the insulation inside of the wall. Last, you call the princ function to enable the AutoLISP routine to exit gracefully without any errors.

Adding a Dialog Box to an AutoLISP Routine

The IACAD14 routine is fairly decent at this point. But, if you have worked in the Windows environment, you are probably used to being able to type most, if not all, the necessary information for the routine in a dialog box.

Adding one or more dialog boxes to an AutoLISP routine is not necessary, but it makes the routine look more professional and, in many cases, makes the routine easy to use and run.


TIP: You should strive to have your AutoLISP routine up and working properly with keyboard entry before you begin to add dialog boxes to it. This makes it easier to debug the routine if errors crop up because of the additional code necessary to create the dialog box(es).

AutoCAD provides you with many tools for creating a dialog box for an AutoLISP routine. But, before you begin to create the dialog box, you must analyze your routine and decide what data the user enters for the routine to work is appropriate for inclusion in the dialog box.

(defun info ()
    (setq start (getpoint "\nChoose the start point of the wall: "))
        (setq end (getpoint "\nChoose the end point of the wall: "))
        (setq width (getdist "\nEnter the width: " start))
        (setq halfwidth (/ width 2))
        (setq wallangle (angle start end))
        (setq walllength (distance start end))
        (setq arcsize (/ width 4))
        (setq perpath (+ wallangle (dtr 90)))
        (setq perpath2 (- wallangle (dtr 90)))
        (setq total 1)
        (setq arcnumber 1)
)

The Info routine from IACAD14 shows you all the information that is necessary for the routine to work. As you can see, the user must supply the start and end points of the wall, as well as the overall width of the wall. The start and end points are implemented as getpoint, which requires the user to select a point on the screen with the mouse. The only other information the user supplies is the width of the wall. All three of these items are good candidates for inclusion in the dialog box.

Deciding which information should be retrieved from the user through a dialog box is solely up to you. You may run across instances in which using a dialog box may prove to be cumbersome or unnecessary because it slows down the use of the routine. For example, the ZI function discussed earlier in this chapter is not a good candidate for a dialog box. There isn't any user input. A command such as ZW zoom window is not good either. ZW requires the user to select the two corner points to perform the Zoom Window option. Using a dialog box here would be a waste of time.

Dialog boxes are implemented in a second file called a DCL file. This file is written in a slightly different syntax, called the Dialog Control Language. What is nice about this dialog language is that it is shared between AutoLISP, ADS, and ARX for creating dialog boxes. The following code segment is necessary for the dialog box you are going to create for this example. Figure 24.12 shows you the resulting dialog box in AutoCAD 14.

Figure 24.12 The Demonstration dialog box created from the code that immediately follows this figure.

cad_box : dialog {
        label = "Wall Insulation Demo for Inside AutoCAD 14"; 
        : boxed_column {
        label = "Start Point";
               : edit_box {
                       label = "   X:";
                       key = "spx";
                       edit_width = 10;
                       }
                : edit_box {
                       label = "   Y:";
                       key = "spy";
                       edit_width = 10;
                       }
               }
        : boxed_column {
        label = "End Point";
               : edit_box {
                       label = "   X:";
                       key = "epx";
                       edit_width = 10;
                       }
                : edit_box {
                       label = "   Y:";
                       key = "epy";
                       edit_width = 10;
                       }
            }
        : edit_box {  
               label = "&Width of Wall";
               key = "ww";
               edit_width = 6;
               }
        :row{
        : button {    
               label = "OK";
               key = "accept";
               width = 8;
               fixed_width = true;
               }
        : button {   
               label = "Cancel";
               is_cancel = true;
               key = "cancel";
               width = 8;
               fixed_width = true;
               }
        }
}

As you can see, the code for the dialog box looks fairly complex, but it is rather simple to use. The DCL language is very similar to C or C++ in terms of syntax. Brackets surround almost all commands {}. For example, the following code segment shows you one single item in the list.

: button {   
               label = "Cancel";
               is_cancel = true;
               key = "cancel";
               width = 8;
               fixed_width = true;
               }

As you might guess, this creates a button on the dialog box. Between the brackets are the options for the button, such as width, label, and most importantly, the key value. The key value is like a variable in a function in AutoLISP. The key is used to access the data stored in the dialog box. This particular button is the Cancel button.

Let's break down the code for this dialog box succinctly.

cad_box : dialog {
        label = "Wall Insulation Demo for Inside AutoCAD 14";

The first two lines of the DCL file used in this chapter are shown in the preceding code. The first line: cad_box : dialog is where you give the dialog box a name to call from your AutoLISP routine, in this case, cad_box. The second line is the label, which is the text that shows in the title bar of the dialog box. These two are always required for a dialog box. Everything else is optional for you to use, except for an OK button. You must always include an OK button to give the user a method for exiting the dialog box.

AutoCAD provides you with a set of predefined dialog box parts (called DCL tiles) that you may use. These include items such as text boxes, radio buttons, plain text, and so on. For the dialog box in this example, you will make use of two boxed columns (the start and end point sections of the dialog box) and two buttons. The following is the code for the Start Point section of the dialog box:

: boxed_column {
        label = "Start Point";
               : edit_box {
                       label = "   X:";
                       key = "spx";
                       edit_width = 10;
                       }
                : edit_box {
                       label = "   Y:";
                       key = "spy";
                       edit_width = 10;
                       }
               }

At the top of the code segment, you will find the boxed column command. Like the dialog box, each boxed column can have its own label, in this case, Start Point. Below that, you see two edit box entries and their properties. The edit boxes give you the type in fields in the dialog box. Each edit box has a label, key value, and width. Additional properties are available if you need them.


NOTE: Refer to the AutoCAD documentation for more on the additional properties of these or any other dialog box feature not covered in this chapter.

Make sure that you note the use of the brackets in this particular code segment. Each edit box has its own set of brackets, as does the boxed column (which contains the edit boxes within its own brackets).

The End Point section is exactly the same. Another edit box is also added to the dialog box, but not inside of a boxed column. This is the width field, as shown in figure 24.12. The last two entries in the code are the OK and Cancel buttons, which are handled very much like the edit boxes.

If you will notice, in the code, AutoCAD creates the dialog box in the order in which the items are presented in the DCL file. You can further control these items by using additional commands such as Column to help lay out the actual location of the DCL tiles.

After you create the code for the dialog box, you must save it in a separate file from the AutoLISP routine with a DCL extension. The file must be located in a directory that is part of the AutoCAD support path or AutoCAD will not be able to load the dialog box. You can control support paths under the Preferences command.

After you save the file, you must add the necessary code and change your AutoLISP routine to make use of the new dialog box.


NOTE: You may need to add the reference to the DCL file to your AutoLISP routine before you get too far into developing the dialog box. You will have to use your AutoLISP routine to access the dialog box and see what it looks like at any given point.

Integrating the Dialog Box Code into the AutoLISP Routine

The following is the wall insulation routine with the new code for the dialog box. New code is shown in bold.

(defun dtr (a)
        (* pi (/ a 180.0))
)
(defun info ()
        (setq start (list (atof spx) (atof spy) 0.0))
        (setq end (list (atof epx) (atof epy) 0.0))
        (setq width (atof ww))
        (setq halfwidth (/ width 2))
        (setq wallangle (angle start end))
        (setq walllength (distance start end))
        (setq arcsize (/ width 4))
        (setq perpath (+ wallangle (dtr 90)))
        (setq perpath2 (- wallangle (dtr 90)))
        (setq total 1)
        (setq arcnumber 1)
)
(defun drawwall ()
        (command "pline"
               (setq corner (polar start perpath halfwidth))
               (setq corner (polar corner wallangle walllength))
               (setq corner (polar corner perpath2 width))
               (polar corner (+ wallangle (dtr 180)) walllength)
               "close"
               )
)
(defun insulate ()
        (setq arcstart1 (polar start perpath2 (/ halfwidth 2)))
        (while (< total walllength)
               (setq arcenter1 (polar arcstart1 wallangle arcsize))
               (setq arcend1 (polar arcenter1 wallangle arcsize))
               (setq arcenter2 (polar arcend1 perpath (* 2                 ¬arcsize)))
               (setq arcstart2 (polar arcenter2 wallangle arcsize))
               (setq arcend2 (polar arcenter2 (+ wallangle (dtr                 ¬180)) arcsize))
               (command "arc" arcstart1 "c" arcenter1 arcend1)
               (command "arc" "c" arcenter2 arcstart2 arcend2)
               (command "line" arcstart2 arcend1 "")
               (command "line" arcend2 arcend1 "")
               (setq arcnumber (1+ arcnumber))
               (setq arclength (distance arcstart1 arcend1))
               (setq arcstart1 (polar arcstart1 wallangle (* 2                 ¬arcsize)))
               (setq total (* arcnumber  arclength))
        )
)
(defun dialdefaults()
        (if (not spx) (setq spx 0.0))
        (if (not spy) (setq spy 0.0))
        (if (not epx) (setq epx 0.0))
        (if (not epy) (setq epy 0.0))
        (if (not ww) (setq ww 0.0))
        (set_tile "spx" (rtos spx))
        (set_tile "spy" (rtos spy))
        (set_tile "epx" (rtos epx))
        (set_tile "epy" (rtos epy))
        (set_tile "ww" (rtos ww))
)
(defun CADDIAL ()
        (setq dcl_id  (load_dialog "iacad14.dcl")) 
        (if (not (new_dialog "cad_box" dcl_id))(exit)) 
        (dialdefaults)
        (action_tile "spx" "(setq spx (get_tile \"spx\"))")
        (action_tile "spy" "(setq spy (get_tile \"spy\"))")
        (action_tile "epx" "(setq epx (get_tile \"epx\"))")
        (action_tile "epy" "(setq epy (get_tile \"epy\"))")
        (action_tile "ww" "(setq ww (get_tile \"ww\"))")
        (action_tile "cancel" "(done_dialog)")
        (action_tile "accept" "(info)(done_dialog)") 
        (start_dialog)
        (princ)
   
)     
      
(defun C:ddia ( / spx spy spz epx epy epz ww)
        (caddial)
        (drawwall)
        (insulate)
        (princ)
) 

As you can see from the code listing, quite a bit of code was added to make the dialog box work. Let's break each new section down, in order of execution. First, the main routine of the program:

(defun C:ddia ( / spx spy spz epx epy epz ww)
        (caddial)
        (drawwall)
        (insulate)
        (princ)
)

The function name has been changed from IACAD14 to DDIA, which is easier to type in and also to add the DD to indicate a dialog box is now being used. (DD is not necessary; it is just standard practice in AutoCAD.)

In the earlier version of this part of the routine, empty braces followed the command. This is not the case in the modified routine. You will notice a / followed by variables. This simply indicates that the variables listed after the / are available only when this particular function is running. If you try to create this routine without limiting the scope where these variables are used, you will only be able to run the routine once per session of AutoCAD.

To make use of dialog boxes and edit boxes, you must enter information as text strings. But, AutoCAD wants to have the numbers you enter as text strings in floating point format, so later, you must convert from one to the other. If you let the variables listed after the / be available after the routine has run, they will be in the wrong format the next time you run the routine and will cause an error. You will see more about this problem later in the chapter.

Notice that the DDIA command now calls a function called caddial (CAD Dialog) now instead of Info. Let's take a look at this new function.

(defun CADDIAL ()
        (setq dcl_id  (load_dialog "iacad14.dcl")) 
        (if (not (new_dialog "cad_box" dcl_id))(exit)) 
        (dialdefaults)
        (action_tile "spx" "(setq spx (get_tile \"spx\"))")
        (action_tile "spy" "(setq spy (get_tile \"spy\"))")
        (action_tile "epx" "(setq epx (get_tile \"epx\"))")
        (action_tile "epy" "(setq epy (get_tile \"epy\"))")
        (action_tile "ww" "(setq ww (get_tile \"ww\"))")
        (action_tile "cancel" "(done_dialog)")
        (action_tile "accept" "(info)(done_dialog)") 
        (start_dialog)
        (princ)
   
)     

Caddial is the main function for creating and handling the dialog box. Notice the first two lines of code in the function. The first line loads the DCL file and assigns its ID (which is defined by AutoCAD at run time) to a variable called dcl_id. The second line verifies that the id is unique, or else the routine exits. Only under rare circumstances will the system incorrectly assign an id and cause and exit here. These two lines plus the (start_dialog) line are all that are necessary to make the dialog box appear. But, additional code is necessary to make the dialog box functional. For each DCL tile (button or text box) in the dialog box, you must assign an event handler so AutoCAD knows what to do with the information the user places in the dialog box.

In the caddial function, this is handled in two ways. First, a function called dialdefaults is called to set all of the tiles in the dialog box to some default value, in this case 0. The following code segment shows you the dialdefaults function.

(defun dialdefaults()
        (if (not spx) (setq spx 0.0))
        (if (not spy) (setq spy 0.0))
        (if (not epx) (setq epx 0.0))
        (if (not epy) (setq epy 0.0))
        (if (not ww) (setq ww 0.0))
        )

The first five lines of the dialdefaults function set the variables spx, spy, epx, epy, and ww to a default of 0.0, if they are not already defined. Spx and spy are the start point x and y values. Epx and epy are the end point x and y values. Ww is the width variable. After the defaults are set, the control of the routine is returned back to the caddial function.

After the control is returned back to caddial, each tile of the dialog box has a handler created, such as the one shown in the following code fragment.

(action_tile "spx" "(setq spx (get_tile \"spx\"))")

Here, the command action_tile is used to control what happens when the user enters or makes use of that particular tile. In this case, the tile is the spx tile. If you look back at the DCL file code, find the tile entry with a value named "spx." This is the edit box referred to here. If the user enters any value into the x coordinate start point box, when he leaves the box by clicking somewhere else in the dialog box, or pressing Tab, the second part of the action_tile command is executed.

Here, the value the user typed in is retrieved from the dialog box by the get_tile function and assigned to the variable spx. You may notice the unusual syntax of the get_tile command. This is necessary because of the way the action_tile command works.

An action_tile command is created for all seven tiles in the dialog box. After the action commands are defined, the dialog box is actually started with (start_dialog), which is the last command of the caddial function.

At this point, you may be wondering how the routine proceeds. The answer is in the OK button and Cancel button action_tiles. The Cancel button action_tile command, as shown in the following code, calls the (done_dialog) command, which terminates the routine.

(action_tile "cancel" "(done_dialog)")
(action_tile "accept" "(info)(done_dialog)")

The OK button, which has a key value of "accept," calls the info routine (which you defined earlier in this chapter), and then calls the done_dialog command to exit the dialog box.

All that is left to complete the sequence is to assign the dialog box values to the variables used in the rest of the routine. This is handled in the first three lines of the Info function:

(setq start (list (atof spx) (atof spy) 0.0))
(setq end (list (atof epx) (atof epy) 0.0))
(setq width (atof ww))

Here, you will notice that the start, end, and width variables used in the rest of the program are defined. Before, user input at the Command: prompt handled these. Now, the values are coming from the dialog box.

A dialog box can only make use of text strings, so each variable from the dialog box must be converted from text to a numerical format--in this case, floating point. The atof command stands for ASCII to Floating Point, which converts the numbers the user typed in into actual coordinates the system can use. Notice the use of the list command. This is necessary to create the start variable as a point in AutoCAD (which is defined by three coordinates). AutoCAD stores points as a list. So, by creating a list of the x, y, and z points and assigning them to the start variable, the routine can correctly process the rest of the commands and create the geometry.

As you can see from the additions necessary to make a dialog box work in AutoLISP, it may not always be to your benefit to create a dialog box. Even the dialog box created in this chapter has limited uses, because the user must type in the start and end point x and y values.

Going Further

For some practice on your own, do a little research in the AutoCAD customization documentation and see if you can add a Pick button that enables you to quickly pick the point on the screen and return you back to the dialog box where the point you picked will be displayed in the edit boxes. For an example of how to do this, check out BMAKE.LSP in the Support directory of AutoCAD.

There are many features that you can make use of in a dialog box in AutoCAD. This section only covered a few basic ones. Explore the documentation shipped with AutoCAD, which is much more in-depth than what is possible to cover in one chapter here.

Error Handling

The last thing to talk about when creating an AutoLISP routine is error handling. This is one of the more important aspects of writing a good AutoLISP routine. Take the routine you have been studying in this chapter, for example. Think about what could go wrong with it, either due to the way you coded it, or how the user will make use of it. If something does go wrong, how will you handle it?

A good example of an error handling routine might be to check and verify that the coordinates typed in by the user are valid coordinates that can be used by the routine. There are any number of methods to implement this type of error checking. All of them are too involved to cover in this chapter.

When you write a good routine, up to one half or more of the actual code written can be dedicated to correct error handling. Most of the time, this will come from testing and more testing of the routine to find its limitations.


NOTE: Error handling is very important in a routine. The one shown in this chapter is functional, but not really error-proof. This is too complex to cover in this book. Read the AutoCAD documentation sections devoted to error handling to get an idea of how to use it. Then, study the AutoLISP routines shipped with AutoCAD. There are many examples of error handling in each one.

Limitations on the Sample AutoLISP Routine

For all the neat things this routine does, it does have a few limitations. They are:

As you can see, you can take an AutoLISP routine and keep adding functionality to it. But, the language is easy enough for you to write simple routines such as this.


TIP: Don't expect to be able to write this routine in a couple of hours and don't expect it to work perfectly the first time. This routine took 4.5 hours to develop and about 20 changes to the source code before it worked correctly every time. But, as you can see, this 4.5 hours can be well spent if you can spare it. Eventually, you will save that much time by using this routine.

Summary

The AutoLISP programming interface is a simple but powerful method for you to customize AutoCAD. If you need customized routines, you should consider developing these routines in-house for you to use. If you use the routines a lot, you can load them every time AutoCAD loads by using an ACAD.LSP file. The only question is whether or not you can justify the amount of time necessary to develop the routine.

For many people, they cannot justify the time, or are simply not interested in programming AutoCAD. Fortunately, a whole industry has developed around the prospect of developing custom software for AutoCAD. These products are commonly called third-party products and can significantly enhance the productivity of AutoCAD.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.